Up until this point, our discussions regarding
BizTalk schemas, deployment, and namespaces have been in the practical
sense. We have not discussed where schemas are actually stored or what
is happening within BizTalk when a new schema is deployed. For that, you
need to understand the BizTalk Management Database. Unlike the
Messagebox, the Management Database is not used to store messages and is
not considered to be "working storage." The Management Database is
where most configuration and system-level information is stored and
accessed from within BizTalk. It is important to understand what's going
on behind the scenes when you perform administrative actions using
either UI tools or the ExplorerOM/WMI API,
because this will give you a good understanding of how BizTalk
artifacts are tied together within the product. Such understanding may
save time when dealing with specific issues such as "Why isn't my schema
namespace resolving?" or "What version of my schema is this document
resolving against?"The first topic we will
cover is how BizTalk Server resolves document/message types and
how/where they are stored in Management Database. If you have worked on a
BizTalk project previously, you no doubt have seen the following error
messages from the BizTalk Messaging Engine:
There
was an error executing receive pipeline: Finding the document
specification by message type {MessageType} failed. Verify the schema
deployed properly.
or
There was a
failure executing receive pipeline: Cannot locate document specification
because multiple schemas matched this message type [MessageType]
Knowing how BizTalk handles message types will help you address this type of problem. We will also provide you with ExplorerOM
code for enumerating deployed document specifications, which you can
use in your own helper/supplementary utilities tailored for your
specific needs.
The second topic we are
going to cover is what's going on in the BizTalk Management Database
when you deploy a BizTalk assembly. These two discussions should help
you troubleshoot any specific issues you may encounter when dealing with
schema/version resolution issues.
Let's first
create an application containing only one schema file and one
orchestration. The document will be received by a receive location,
validated against a schema by a pipeline, and routed to the
orchestration. Open Visual Studio, and create an empty solution with an
appropriate name. Add two BizTalk projects to the solution, and name
them Schemas and Orchestrations. In the Visual Studio Explorer,
right-click the project name, select the Properties menu item, switch to
the Signing tab, and select the Sign the Assembly check box. Specify an
existing, or generate a new, strong name key file.
Choose Add a New Project
Item, and then choose schema file. Name it SampleSchema. Switch to the
Properties window, select the root node, and change the Node Name
property to Sample, as shown in Figure 1.
Now switch to Solution
Explorer, right-click the Schema project, and select Build. BizTalk
compiles the XSD schema and places class definitions in its Schemas.dll
assembly. You can see it in the project's output directory. If you are
curious, you can load the generated assembly Schemas.dll file into a
freeware tool such as the .NET Assembly Viewer from Ralf Reiterer to check out the generated .NET class definition matching our SampleSchema.xsd schema, as shown in Figure 2.
Switch to the
Orchestrations project, add an orchestration, and configure it to pass
through any received messages to a file-based send port, as shown in Figure 3.
Despite that this is definitely not the best practice of using
orchestrations, let's accept such a violation for the purpose of this
discussion.
Right-click the
Orchestrations project, and select the Build menu item. As you may have
guessed, the XLANG compiler compiles the orchestration into a C# class
and calls the standard csc.exe C# compiler. This in turn generates the
Orchestrations.dll assembly. Now that all the preliminary steps are
complete, you are ready to deploy the solution. Choose the Build menu,
and select Deploy Solution. Executing the Deploy command within Visual
Studio puts assemblies into the GAC (if this option is selected in the
project settings) and also updates the Management Database. The first
table you want to examine within the BizTalk Management Database is
bts_Assembly. This table contains information about system assemblies
and assemblies specific to custom applications. They are distinguished
by an nSystemAssembly field. A value of 1 in this column indicates that
assembly is a system assembly. As you can see in the last row, the
deployment procedure put information about the Schemas and
Orchestrations assemblies into this table, as shown in Figure 4.
Next, you'll figure
out what your message type is. A
message type within a BizTalk solution is typically a concatenation of
target namespace property and root node name separated by # sign. When
you add a new schema file to your project, Visual Studio automatically
sets the target namespace to http://[ProjectName].[SchemaFileName].
The schema's root node name is always preset to Root. When you deploy
your project containing XSD schemas, the deployment procedure puts the
message types into and references the assembly containing the .NET class
definitions in bt_DocumentSchemas table, which is used by BizTalk to
store and retrieve the information about deployed system-wide schemas
and the schemas pertaining to custom applications.
Since you changed the root node name in your project to Sample, the message type will be http://Schemas.SampleSchema#Sample.
Open the SQL Server Management Studio, and run the query against the bt_DocumentSpec table, as shown in Figure 5.
The output of this query confirms that you defined a new message of type http://Schemas.SampleSchema#Sample, and the class SampleSchema is representing your schema. Its location is in the assembly Schemas, version 1.0.0.0.
Upon startup, the BizTalk
runtime caches this table in memory. When a Receive Adapter passes an
inbound message to a disassembling receive pipeline, it first constructs
the message type by concatenating the namespace and root node name of
the incoming document. It then checks whether the message type is
uniquely presented in the bt_DocumentSpec table. If the check is
successful, the receive pipeline proceeds with further execution; if
not, you will get an error message reporting an inability to resolve the
message type. When you use Visual Studio's automatically generated
unique namespace for document specification, you won't run into
problems. But since you very likely will change the Target Namespace and
Root Node Name properties manually, for example, to follow namespace
pattern for all artifacts in a solution and to give the root nodes
meaningful names, you have to be careful not to create conflicting
document specifications. The problem is that deployment procedure
doesn't check whether the document specification with the same namespace
and root node type name has already been deployed and therefore doesn't
produce any warning or error message in case of duplication.
Even if you create and
maintain XSD specifications carefully, there is a situation you should
be aware of that could potentially lead to problems. While working on
different projects, we have seen developers make the same mistake of
ending up with duplicate document specifications and then spending hours
trying to figure out what's going on and why BizTalk refuses to process
the document complaining that multiple schemas are matching the same
message type. Here is how this might happen.
As you may already know,
the BizTalk programming model is quite flexible, especially when it
comes to message type conversion. When you call methods located in
external .NET assemblies from your orchestration and pass the BizTalk
message to those methods, you most likely pass the message as either a
System.Xml.XmlDocument type or a Microsoft.XLANGs.
BaseTypes.XLANGMessage type. You can then extract the message in the
form of a well-typed .NET class, which is very convenient. The inverse
applies to when you need to create a new message inside your .NET code
and return it to your orchestration. In your orchestration inside a
Construct Message shape, you can call a method that returns a .NET class
representing an orchestration message. To generate this typed class,
which matches your XSD schema, launch the Visual Studio command prompt,
and then run the following command:
- Xsd.exe /c SampleSchema.xsd
This command will
generate a file named SampleSchema.cs containing the class named after
the root node of the schema, which is Sample. Add a new class library
project named SchemaClasses to the solution, and include this file to
the project. Now you can extract messages and create new ones as a
well-formed type, as in Listing 1.
Example 1. Type Orchestration Message
using System; using System.Collections.Generic; using System.Text; using Microsoft.XLANGs.BaseTypes;
namespace CustomClasses { public class MyCustomSampleClass {
public Sample CreateNewMessage(XLANGMessage sourceMessage) { Sample srcClass = sourceMessage[0].RetrieveAs(typeof(Sample)); Sample newClass = new Sample(); newClass = srcClass; return newClass;
} } }
|
Surely this is quite a
handy technique, but like all conveniences, it can be a source of
problems if used indiscriminately. When you declare a message in an
orchestration, you are required to specify what type of message it is
(not to be confused with the message type routing property). You have
four options:
.NET classes
Multipart message types
Schemas
Web message types
.NET types can be cast to an
orchestration message type. So, having both .NET classes produced by the
XSD.EXE tool and XSD schemas in the same solution technically allows
you to choose either when specifying the type of message or when
creating new orchestration message variables.
In your Orchestration project,
add a reference to the SchemaClasses project. Then add a new message
variable to the orchestration, switch to the Properties View, and select
the Message Type combo box. Expand the .NET class branch, and choose
Select from Referenced Assembly. This will pop up a Select Artifact Type
dialog box, as shown in Figure 6.
Select the Sample item in the
right pane, and compile and deploy the orchestration. Now open the SQL
Server Management Studio, run the same query as shown in Figure 3-20 against the bt_DocumentSpec table, and check out your results. As you can see in Figure 7,
the document specification is deployed twice, pointing to different
assemblies: one pointing to the assembly created when you deployed XSD
schemas and another to the assembly containing classes produced by the
XSD.EXE tool.
As you see, the
deployment procedure makes no attempts to check for possible duplication
and simply deploys everything as is. If you now submit a document
instance to the receive location, you will get the following error
message:
There was
a failure executing receive pipeline: Cannot locate document
specification because multiple schemas matched this message type
[MessageType]
In fact, you can achieve the
same result with much less effort, and it is also a real-world scenario.
After deploying the Schemas assembly, go to the project's settings,
specify a different output assembly name, and redeploy the project. In
the Management Database, you will end up having two document
specifications with the same message type located in different
assemblies. Moreover, as of this writing, the product is still in beta,
and the MMC crashes when you attempt to see deployed schemas if there
are schemas with duplicate message types. So, familiarity with the major
tables in BizTalk Management Database may be quite helpful.
Listing 2
shows how you can enumerate deployed assemblies and schemas. The
product documentation doesn't mention the important properties of the SchemaTargetNamespace and RootNode, which together constitute a message type. So, be aware that these properties are available. class, namely,
Example 2. Enumerating Deployed Assemblies
using System; using System.Text; using Microsoft.BizTalk.ExplorerOM;
namespace Schemas { class Program {
static void Main(string[] args) { EnumerateSchemas(); Console.ReadKey(); }
public static void EnumerateSchemas() { BtsCatalogExplorer catalog = new BtsCatalogExplorer(); catalog.ConnectionString = "Server=.;Initial Catalog=BizTalkMgmtDb;Integrated Security=SSPI;";_
foreach (BtsAssembly assembly in catalog.Assemblies ) {
foreach (Schema schema in assembly.Schemas) { Console.WriteLine("\t{0}#{1}", schema.TargetNameSpace,schema.RootName );
} } } } }
|
Or you can access deployed schemas directly without enumerating assemblies first, as shown in Listing 3.
Example 3. Accessing Deployed Schemas Directly
using System;
using System.Text;
using Microsoft.BizTalk.ExplorerOM;
namespace Schemas
{
class Program
{
static void Main(string[] args)
{
EnumerateSchemas();
Console.ReadKey();
}
public static void EnumerateSchemas()
{
BtsCatalogExplorer catalog = new BtsCatalogExplorer();
catalog.ConnectionString = "Server=.;Initial
Catalog=BizTalkMgmtDb;Integrated Security=SSPI;";
foreach (Schema schema in catalog.Schemas)
{
Console.WriteLine("\t{0}#{1}",
schema.TargetNameSpace,schema.RootName);
}
}
}
}